package pCloud.message;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Set;

import com.sun.xacml.Indenter;
import com.sun.xacml.attr.StringAttribute;
import com.sun.xacml.ctx.Attribute;
import com.sun.xacml.ctx.RequestCtx;
import com.sun.xacml.ctx.Subject;

/**
 * Represent the file being exchanged over the socket. For simplicity, we
 * convert the file into an array of String. At the receiving end, the server
 * reconstruct a file from this content.
 * 
 * Two type of files being exchange: request and policy (for access control)
 * 
 */
public class SocketFile implements Serializable {
	public static final int REQUEST_FILE = 1;
	public static final int POLICY_FILE = 2;
	int type = -1;

	ArrayList<String> content;

	public SocketFile() {
		this.content = new ArrayList<String>();
	}

	/**
	 * request or policy file
	 */
	public int getType() {
		return this.type;
	}

	/**
	 * Initializing the content from request context (read from file). We use
	 * the content file as index for a hashmap. A problem occurs in if we are to
	 * use the existing XACML implementation for exporting the RequestCtx object
	 * to a file, since they use Set to store the attributes. When attributes
	 * are being printed, different sets having the same elements may have
	 * different order of outputs, which make the output file completely
	 * different (lines arranged in different order results in different
	 * hashcodes).
	 * 
	 * We deal with this by sorting the output from existing XACML
	 * implementation before adding them to the content of the final output
	 * file.
	 * 
	 * @param requestCtx
	 */
	public void initializeContentFromRequestCtx(RequestCtx requestCtx) {
		Indenter indenter = new Indenter();

		// Prepare the indentation string
		String topIndent = indenter.makeString();
		content.add(topIndent + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
		content.add(topIndent
				+ "<Request xmlns=\"urn:oasis:names:tc:xacml:1.0:context\">\n");

		// go in one more for next-level elements...
		indenter.in();
		String indent = indenter.makeString();

		// ...and go in again for everything else
		indenter.in();

		// first off, go through all subjects
		Iterator it = requestCtx.getSubjects().iterator();
		while (it.hasNext()) {
			Subject subject = (Subject) (it.next());
			content.add(indent + "<Subject>");

			Set subjectAttrs = subject.getAttributes();

			if (subjectAttrs.size() == 0) {
				// there's nothing in this Subject, so just close the tag
				content.add("/>\n");
			} else {
				// there's content, so fill it in
				content.add("\n");

				encodeAttributes(subjectAttrs, content, indenter);

				content.add(indent + "</Subject>\n");
			}
		}

		// next do the resource
		if ((requestCtx.getResource().size() != 0)) {
			content.add(indent + "<Resource>\n");
			encodeAttributes(requestCtx.getResource(), content, indenter);
			content.add(indent + "</Resource>\n");
		} else {
			content.add(indent + "<Resource/>\n");
		}

		// now the action
		if (requestCtx.getAction().size() != 0) {
			content.add(indent + "<Action>\n");
			encodeAttributes(requestCtx.getAction(), content, indenter);
			content.add(indent + "</Action>\n");
		} else {
			content.add(indent + "<Action/>\n");
		}

		// finally the environment, if there are any attrs
		if (requestCtx.getEnvironmentAttributes().size() != 0) {
			content.add(indent + "<Environment>\n");
			encodeAttributes(requestCtx.getEnvironmentAttributes(), content,
					indenter);
			content.add(indent + "</Environment>\n");
		}

		// we're back to the top
		indenter.out();
		indenter.out();

		content.add(topIndent + "</Request>\n");
	}

	private void encodeAttributes(Set attributes, ArrayList<String> out,
			Indenter indenter) {

		Iterator it = attributes.iterator();
		ArrayList<String> tempOut = new ArrayList<String>();

		while (it.hasNext()) {
			Attribute att = (Attribute) it.next();
			att.encode(tempOut, indenter);
		}

		// sorting the output
		Object[] temp = tempOut.toArray();
		Arrays.sort(temp);

		for (int i = 0; i < temp.length; i++)
			out.add((String) temp[i]);
	}

	public void initializeContentFromFile(String fileName) throws IOException {
		BufferedReader reader = new BufferedReader(new FileReader(fileName));
		String line;
		while ((line = reader.readLine()) != null)
			content.add(line);
		reader.close();
	}

	public File reconstructFileFromContent(String fileName, int index)
			throws IOException {
		PrintWriter writer = new PrintWriter(new FileWriter(fileName + "_"
				+ index));
		int size = content.size();
		for (int i = 0; i < size; i++)
			writer.write(content.get(i) + "\n");
		writer.close();
		return new File(fileName + "_" + index);
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((content == null) ? 0 : content.hashCode());
		result = prime * result + type;
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		SocketFile other = (SocketFile) obj;
		if (content == null) {
			if (other.content != null)
				return false;
		} else if (!content.equals(other.content))
			return false;
		if (type != other.type)
			return false;
		return true;
	}

}
